package com.clickpaas.governmentcloud.utils.loggerutils;

import com.clickpaas.governmentcloud.common.ServerResponse;
import com.clickpaas.governmentcloud.utils.entityutils.ConditionalNotEmpty;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 
 * 接口日志切面类,环绕通知
 * 
 * @author quhongwei E-mail:quhongwei@cs91tech.com
 * @created 2020-09-17 下午5:02:47 
 * @version 1.0
 */
@Aspect
@Component
public class LoggerAspect {
	
	public static final Logger logger = LoggerFactory.getLogger(LoggerAspect.class);
	
	@Pointcut("@annotation(com.clickpaas.governmentcloud.utils.loggerutils.Logger)")
	public void annotationPointcut() {
		
	}
	
	@Around("annotationPointcut() && @annotation(param)")
	public ServerResponse<?> doAround(ProceedingJoinPoint joinPoint, com.clickpaas.governmentcloud.utils.loggerutils.Logger param) {
		long startTime = System.currentTimeMillis();
		ServerResponse<?> serverResponse = ServerResponse.createError("查询失败");
		try {
			// 接口调用时间
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
			Date startDate = new Date();
	    	String currTime = sdf.format(startDate);
	    	logger.info("-------start------");
			
	    	// 获取接口方法名
			MethodSignature sign = (MethodSignature) joinPoint.getSignature();
	        Method method = sign.getMethod();
			String description = param.description();
			
			if("null".equals(description)) {
				ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
				if(apiOperation != null) {
					description = apiOperation.value();
					if(description == null || "".equals(description)) {
						description = apiOperation.notes();
						if(description == null || "".equals(description)) {
							description = "暂无描述，请在@Logger注解中添加description参数,或者在@ApiOperation注解中对value或notes设值";
						}
					}
				} else {
					description = "暂无描述，请在@Logger注解中添加description参数";
				}
			}
			
			logger.info("接口调用时间: " + currTime);
			logger.info("接口描述: " + description);
			logger.info("接口名称: " + method.getName());
			
			// 获取参数
			Object[] args = joinPoint.getArgs();
			if(args != null && args.length > 0) {
				for(int i = 0; i < args.length; i++) {
					logger.info("参数[" + i + "]为: " + args[i].toString());
				}
			} else {
				logger.info("接口参数为: null");
			}
			
			// 判断 Logger.condition() 是否等于true，开启参数校验
			boolean condition = param.condition();
			if(condition) {
				// 校验对象参数切面
				serverResponse = conditionalNotEmpty(args, joinPoint);
			} else {
				// 调用方法,并获取返回值 
				serverResponse = (ServerResponse<?>) joinPoint.proceed(args);
			}
			
			// 添加计算耗时日志
			printTime(startTime);
			
		} catch (Exception e) {
			e.printStackTrace();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return serverResponse;
	}
	
	public static void printTime(long startTime) {
		// 添加计算耗时日志
		long endTime = System.currentTimeMillis();
		long secondsTime = (endTime - startTime) / 1000;
		
		Integer seconds = Integer.parseInt(String.valueOf(secondsTime));
		int day=0, hours=0, minutes=0;
		day = seconds / (60 * 60 * 24);
		seconds -= day * 60 * 60 * 24;
		hours = seconds / (60 * 60);
		seconds -= hours * 60 * 60;
		minutes = seconds / 60;
		seconds -= minutes * 60;
		if(day != 0) {
			logger.info("总共耗时: " + day + "天" + hours + "小时" + minutes + "分钟" + seconds + "秒");
		} else if(hours != 0) {
			logger.info("总共耗时: " + hours + "小时" + minutes + "分钟" + seconds + "秒");
		} else if(minutes != 0) {
			logger.info("总共耗时: " + minutes + "分钟" + seconds + "秒");
		} else if(seconds != 0) {
			logger.info("总共耗时: " + seconds + "秒");
		} else {
			logger.info("总耗时不到1秒");
		}
		
		logger.info("-------end------");
	}
	
	public ServerResponse<?> conditionalNotEmpty(Object[] params, ProceedingJoinPoint joinPoint) {
		ServerResponse<?> serverResponse = ServerResponse.createError("失败");
		//切入点方法参数
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        //获取方法参数类型
        Class<?>[] paramsType = method.getParameterTypes();
        //循环参数
    	for(int i = 0; i < params.length; i++) {
    		Object param = params[i];
    		//参数是List
            if (param instanceof List) {
                List<?> list = (List<?>) param;
                for (int j = 0; j < list.size(); j++) {
                	Object l = list.get(j);
                	Class<?> classObj = l.getClass();
                	//反射所有字段
                    Field[] fieldslist = classObj.getDeclaredFields();
                    for(Field field : fieldslist){
                    	ConditionalNotEmpty conditionalNotEmpty = field.getAnnotation(ConditionalNotEmpty.class);
                        //如果字段上有自定义注解@ConditionalNotEmpty
                        if(conditionalNotEmpty != null){
                            field.setAccessible(true);
                            try {
                                Object obj = field.get(l);
                                if(obj == null || "".equals(obj.toString())) {
                                	Map<String, Object> resultMap = new HashMap<>();
                                	String fieldName = field.getName();
                                	String message = conditionalNotEmpty.errorMessage();
                                	logger.info("参数[" + i + "], 索引为[" + j + "]的元素, 字段" + fieldName + "" + message);
                                	resultMap.put("fieldName", fieldName);
                                	serverResponse = ServerResponse.createError("参数[" + i + "], 索引为[" + j + "]的元素, 字段" + fieldName + "" + message);
                                	return serverResponse;
                                }
//                                serverResponse = (ServerResponse<?>) joinPoint.proceed(params);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (Throwable e) {
                            	e.printStackTrace();
                            }
                        }
                    }
                }
            } else {//参数非list
                //获取该参数类型
                Class<?> clazz = paramsType[i];
                //反射所有字段
                Field[] fields = clazz.getDeclaredFields();
                for(Field field : fields){
                	ConditionalNotEmpty conditionalNotEmpty = field.getAnnotation(ConditionalNotEmpty.class);
                    //如果字段上有自定义注解@ValidateObj
                    if(conditionalNotEmpty != null){
                        field.setAccessible(true);
                        try {
                            Object obj = field.get(param);
                            if(obj == null || "".equals(obj.toString())) {
                            	Map<String, Object> resultMap = new HashMap<>();
                            	String fieldName = field.getName();
                            	String message = conditionalNotEmpty.errorMessage();
                            	System.out.println("参数["+i+"]中字段" + fieldName + "" + message);
                            	resultMap.put("fieldName", fieldName);
                            	serverResponse = ServerResponse.createError("参数["+i+"]中字段" + fieldName + "" + message);
                            	return serverResponse;
                            }
//                            serverResponse = (ServerResponse<?>) joinPoint.proceed(params);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (Throwable e) {
                        	e.printStackTrace();
                        }
                    }
                }
            }
    	}
    	try {
			serverResponse = (ServerResponse<?>) joinPoint.proceed(params);
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return serverResponse;
	}
	
}
